package com.tang.script.command;

import com.tang.script.extrator.ConstantsExtractor;
import com.tang.script.extrator.FunctionsExtractor;
import com.tang.script.extrator.VariablesExtractor;
import com.tang.script.functions.FunctionsLinker;
import com.tang.script.variable.Constant;
import com.tang.script.variable.VariablesContainer;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import javax.script.CompiledScript;
import javax.script.ScriptException;
import java.util.LinkedList;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Setter
@Getter
public class SQLCommand extends AbstractScriptCommand<String> {
    @Builder
    public SQLCommand(String scriptText, VariablesContainer variablesContainer) {
        super(scriptText, variablesContainer);
    }

    @Override
    public String execute() {
        initCommand();
        ConstantsExtractor constantsExtractor = ConstantsExtractor.get();
        constantsExtractor.extracte(this);

        VariablesExtractor variablesExtractor = VariablesExtractor.get();
        variablesExtractor.extracte(this);

        FunctionsExtractor functionsExtractor = FunctionsExtractor.get();
        functionsExtractor.extracte(this);

        FunctionsLinker functionsLinker = FunctionsLinker.get();
        functionsLinker.linkFunctions(this);

        defineVariables();

        return doExecuteSQL();
    }

    private String doExecuteSQL() {
        StringBuilder sb = executeFunctions();
        sb = replaceVariables(sb);
        return sb.toString();
    }

    private StringBuilder replaceVariables(StringBuilder sb) {
        constants.values().forEach(constant -> {
            int index = sb.indexOf(constant.getConstName() + " ");
            if(index == -1) return;
            Object value = variablesContainer.getValue(constant);
            String strValue = "";
            if(Objects.nonNull(value)) strValue = value.toString();
            sb.replace(index, index + constant.getConstName().length() + 1, strValue);
        });
        variables.values().forEach(variable -> {
            int index = sb.indexOf(variable.getVarName() + " ");
            if(index == -1) return;
            Object value = variablesContainer.getValue(variable);
            String strValue = "";
            if(Objects.nonNull(value)) strValue = value.toString();
            sb.replace(index, index + variable.getVarName().length() + 1, strValue);
        });
        return sb;
    }

    private StringBuilder executeFunctions() {
        StringBuilder sb = new StringBuilder(this.getResolvedScriptText());
//        Pattern pattern = Pattern.compile("\\w+\\.\\w+\\([\\w\\d \\,]+\\)");
        Pattern pattern = Pattern.compile("\\w+\\.\\w+\\([\\'\\\"\\w\\d \\,\\u4e00-\\u9fa5]+\\)");
        Matcher matcher = pattern.matcher(this.getResolvedScriptText());


        LinkedList<Matched> matcheds = new LinkedList<>();
        while (matcher.find()) {
            String group = matcher.group();
            int start = matcher.start();
            int end = matcher.end();
            matcheds.add(new Matched(start, end, group));
        }
        while (!matcheds.isEmpty()) {
            Matched matched = matcheds.removeLast();
            sb.replace(matched.getStart(), matched.getEnd(), executeFunction(matched.getContent()));
        }
        return sb;
    }

    private String executeFunction(String content) {
        CompiledScript compiledScript = getGroovyShell(content);
        try {
            Object result = compiledScript.eval(binding);
            if (Objects.isNull(result)) return "";
            return result.toString();
        } catch (ScriptException e) {
            throw new RuntimeException(e);
        }
    }

    @Getter
    @Setter
    @AllArgsConstructor
    static class Matched {
        int start;
        int end;
        String content;
    }

    @Override
    public Class getResultClass() {
        return String.class;
    }
}
